/**
 * @author Rid King
 * @since 2017-07-15
 * @version 2.0.0
 * @description 解析类
 */

import Node from './node.js'
import ElementNode from './element-node.js'
import TextNode from './text-node.js'

class HtmlParser {
  /**
   * @description 分解模板,生成间隔字符串数组
   * @param template <String> // 模板字符串
   * @return <Array>
   */
  static decompose(template) {
    let parser = new HtmlParser()
    return parser.resolveNodes(template)
  }

  /**
   * @description 解析为获取结点树对象
   * @param template <String> // 模板字符串
   * @return <Object>
   */
  resolveNodes(template) {
    // 避免模板非独立元素
    template = '<div class="dog-node-container">' + template + '</div>'
    this.template = template

    // 解析为节点对象
    let node = this.resolveNode()

    // 子节点为独立节点时，取子节点
    if (node.childNodes.length === 1) {
      let childNode = node.childNodes[0]
      childNode.parentNode = null
      node = childNode
    }

    return node
  }

  /**
   * @description 解析为结点树对象
   * @param node <String> // 模板字符串
   * @return <Object>
   */
  resolveNode(node) {
    // 定义节点对象
    node = node || new ElementNode

    // 模板不为字符串返回空对象
    if (typeof this.template !== 'string') {
      return node
    }

    let index = 0 // 字符索引
    let isChars = false // 是否是文本
    let lc = null // 正则匹配
    let lm = null // 正则匹配
    let rc = null // 正则匹配

    // 循环检测字符串
    while (this.template.length > 0 && !node.isClosed()) {
      // 标签开始正则
      let startTagReg = /^<([^>\s\/]+)((\s+[^=>\s]+(\s*=\s*((\"[^"]*\")|(\'[^']*\')|[^>\s]+))?)*)\s*(\/?)\s*>/m
      // 标签结束正则
      let endTagReg = /^<\/([^>\s]+)[^>]*>/m
      // 模板字符
      let template = this.template

      // 注释
      if (template.substring(0, 4) === '<!--') {
        // 获取注释闭合索引
        index = template.indexOf('-->')
        // 索引值存在,判断为非文本元素
        if (index !== -1) {
          // 注释处理
          this.parseComment(node, template.substring(4, index))

          // 模板字符重新截取
          this.template = template.substring(index + 3)
          // 非文本元素
          isChars = false
        }
        // 文本元素
        else {
          isChars = true
        }
      }

      // 标签闭合
      else if (template.substring(0, 2) === '</') {
        if (endTagReg.test(template)) {
          lc = RegExp.leftContext
          lm = RegExp.lastMatch
          rc = RegExp.rightContext

          // 处理匹配的标签名
          lm.replace(endTagReg, (sTag, tagName) => {
            // 解析闭合
            return this.parseEndTag(node, tagName)
          })

          // 模板字符重新截取
          this.template = rc
          // 非文本元素
          isChars = false
        }
        // 文本元素
        else {
          isChars = true
        }
      }

      // 标签开始
      else if (template.charAt(0) === '<') {
        if (startTagReg.test(template)) {
          lc = RegExp.leftContext
          lm = RegExp.lastMatch
          rc = RegExp.rightContext

          // 处理匹配的标签名
          lm.replace(startTagReg, (tag, tagName, attrGroup, endTag) => {
            return this.parseStartTag(node, tag, tagName, attrGroup, endTag)
          })

          // 模板字符重新截取，含有子节点时，模板已被修改，则不再设置
          if (node.childNodes.length < 1) {
            this.template = rc
          }
          isChars = false
        }
        // 文本元素
        else {
          isChars = true
        }
      }

      // 文本元素处理
      if (isChars) {
        index = template.indexOf('<')
        // 已经到达末尾
        if (index === -1) {
          this.parseCharacters(node, template)
          this.template = ''
        }
        // 取下一个标签之前的文本元素
        else {
          this.parseCharacters(node, template.substring(0, index))

          // 模板字符重新截取
          this.template = template.substring(index)
        }
      }

      isChars = true
    }

    return node
  }

  /**
   * @description 解析标签结束
   * @param node <Object> // 节点对象
   * @param tagName <String> // 标签名称
   */
  parseEndTag(node, tagName) {
    if (node) {
      // 节点关闭
      node.close()
      // 闭合标签名与该节点标签名不同时，向上级节点抛出
      if (tagName && node.tagName !== tagName) {
        this.parseEndTag(node.parentNode, tagName)
      }
    }
  }

  /**
   * @description 解析标签开始
   * @param node <Object> // 节点对象
   */
  parseStartTag(node, tag, tagName, attrGroup, endTag) {
    // 节点无标签名时，设置节点名
    if (!node.tagName) {
      node.tagName = tagName

      // 解析属性
      let attrs = this.parseAttributes(tagName, attrGroup)
      attrs.forEach(item => {
        node.attributes[item.name] = item.value
      })

      // 主动闭合
      if (endTag === ' /' || /\/\s*\>$/m.test(tag)) {
        node.close()
      }
      // 自闭合标签
      else {
        let aloneTags = [
          'area', 'base', 'br', 'col', 'command', 'embed',
          'hr', 'img', 'input', 'keygen', 'link',
          'meta', 'param', 'source', 'track', 'wbr'
        ]
        aloneTags.forEach(item => {
          // 当前标签名属于自闭合标签的，自动关闭
          if (tagName.toLowerCase() === item) {
            node.close()
          }
        })
      }
    }
    // 节点有标签名时，添加下级节点
    else {
      // 定义节点对象
      let childNode = new ElementNode
      // 设置父节点
      childNode.parentNode = node
      // 父节点添加子节点
      node.childNodes.push(childNode)
      // 解析模板
      this.resolveNode(childNode)
    }
  }

  /**
   * @description 解析属性组
   * @param tagName <String> // 标签名
   * @param attrGroup <String> // 属性字符组
   * @return <Array>
   */
  parseAttributes(tagName, attrGroup) {
    // 属性正则
    let attrReg = /([^=\s]+)(\s*=\s*((\"([^"]*)\")|(\'([^']*)\')|[^>\s]+))?/gm
    let attrs = []

    // 属性字符组遍历
    attrGroup.replace(attrReg, (a0, a1, a2, a3, a4, a5, a6) => {
      attrs.push(this.parseAttribute(tagName, a0, a1, a2, a3, a4, a5, a6))
    })

    return attrs
  }

  /**
   * @description 解析单个属性
   * @param tagName <String> // 标签名
   * @param attr <String> // 属性字符
   * @return <Object>
   */
  parseAttribute(tagName, attr, name) {
    let value = ''
    if (arguments[7]) {
      value = arguments[8]
    } else if (arguments[5]) {
      value = arguments[6]
    } else if (arguments[3]) {
      value = arguments[4]
    }

    return {
      name: name,
      value: !value && !arguments[3] ? '' : value
    }
  }

  /**
   * @description 解析文本元素
   * @param node <Object> // 节点对象
   * @param chars <String> // 文本
   */
  parseCharacters(node, chars) {
    chars = chars.trim()
    // 去除空格等无效元素
    if (chars) {
      let textNode = new TextNode(chars)
      node.childNodes.push(textNode)
      // 设置父节点
      textNode.parentNode = node
    }
  }

  /**
   * @description 解析注释
   * @param node <Object> // 节点对象
   * @param comment <String> // 注释内容
   */
  parseComment(node, comment) {

  }
}

export default HtmlParser
export {
  Node,
  ElementNode,
  TextNode
}